<?php

namespace App\Http\Middleware;

use App\Kuafu\WaiMai\Channel\DyWaiMai;
use App\Kuafu\WaiMai\Enum\DyWaiMaiEnum;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Model\WaiMaiCallbackLogModel;
use App\Kuafu\WaiMai\Model\WaiMaiOrderIdShopIdMappingModel;
use App\Kuafu\WaiMai\Service\BaseWaiMaiOrderService;
use App\Kuafu\WaiMai\Service\WaiMaiShopService;
use App\Library\FeiShu\SendFs;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Kuafu\GeneralFs\BuildFs;

class DyWaiMaiCheckSign
{
    private int $sourceType = WaiMaiDBEnum::SOURCE_TYPE_DOUYIN_WM;

    /**
     * 抖音外卖回调接口 - 日志、校验签名
     *
     * @param Request $request
     * @param Closure $next
     * @return mixed
     * @throws \Exception
     */
    public function handle(Request $request, Closure $next)
    {
        $requestData = [
            'body'   => $request->all(),
            'header' => $request->header(),
            'url'    => $request->url(),
            'query'  => $request->query(),
        ];
        Log::channel('waiMai')->info("DyWaiMaiCheckSign start: ", $requestData);

        // 请求数据为空的，直接返回
        if (!$request->all()) {
            return response()->json(['data' => 'ok'])->header('Content-Type', 'application/json;charset=utf-8');
        }

        $contentJson = $request->get('content', '');//消息内容，根据需要解析消息内容，不同类型的消息内容不同
        $content     = $contentJson && is_array($contentJson) ? $contentJson : json_decode($contentJson, true);

        // 日志入库
        $route   = $request->getPathInfo();
        $orderId = $thirdShopId = 0;
        $msgId   = (string)$request->header('Msg-Id', '');//同一实体下同一action的msg_id相同，服务商可根据msg_id对于消息去重
        if (!$msgId) {
            $msgId = (string)$request->header('msg-id', '');//msgId
        }
        if (!$msgId) {
            $msgId = md5(json_encode($requestData));//msgId
        }
        if (strpos($route, '/api/pay/callback/dyWaiMai/takeoutRefundNotice') !== false) {// 用户申请退款消息 spi地址
            $logId          = (string)$request->header('x-bytedance-logid', '');//请求logid，用于问题排查。
            $xLifeClientkey = (string)$request->header('x-life-clientkey', '');//服务商应用的client_key 无用处 仅作应用标识用
            $sign           = (string)$request->header('x-life-sign', '');//本次请求的sha256签名,用于新版签名验签
            $orderId        = (string)$request->get('order_id', '');
            $thirdShopId    = (string)$request->get('poi_id', '');
            $timestamp      = (string)$request->get('timestamp', '');

            if (self::checkMsgId($request, $msgId)) {
                return response()->json(['data' => 'ok'])->header('Content-Type', 'application/json;charset=utf-8');
            }

            if (!self::checkSign($request, $route, $sign, $logId, $timestamp, $msgId)) {
                return response()->json(['data' => 'ok'])->header('Content-Type', 'application/json;charset=utf-8');
            }
        } else {// 其他回调地址
            $sign  = (string)$request->header('X-Douyin-Signature', '');//抖音侧签名，服务商可根据签名判断该消息是否来自抖音开放平台
            $logId = (string)$request->get('log_id', '');//抖音内部日志id，可提供给抖音方便排查问题
            $event = (string)$request->get('event', '');//消息类型，用于区分各类消息

            if (!$event) {
                $event = $request->all()['request']['event'] ?? "";
            }
            if ($event == DyWaiMaiEnum::VERIFY_WEBHOOK) {// 开发配置>Webhooks验证
                $content = $request->all()['content'] ?? "";
                return response()->json($content)->header('Content-Type', 'application/json;charset=utf-8');
            }

            if (self::checkMsgId($request, $msgId)) {
                return response()->json(['data' => 'ok'])->header('Content-Type', 'application/json;charset=utf-8');
            }

            if (!self::checkSign($request, $route, $sign, $logId, '', $msgId)) {
                return response()->json(['data' => 'ok'])->header('Content-Type', 'application/json;charset=utf-8');
            }

            if ($event == DyWaiMaiEnum::PAY_SUCCESS_ORDER || $event == DyWaiMaiEnum::ORDER_NOTICE) {
                $orderId     = (string)($content['order']['order_id'] ?? '');
                $thirdShopId = (string)($content['poi']['poi_id'] ?? '');
            } elseif ($event == DyWaiMaiEnum::ORDER_FINISH) {
                $orderId     = (string)($content['OrderId'] ?? '');
                $thirdShopId = (string)($content['PoiId'] ?? '');
            } elseif ($event == DyWaiMaiEnum::ORDER_BILL_CREATE) {
                $orderId     = (string)($content['order']['order_id'] ?? '');
                $thirdShopId = (string)($content['poi_id'] ?? '');
            } else {
                $orderId     = (string)($content['order_id'] ?? '');
                $thirdShopId = (string)($content['poi_id'] ?? '');
            }
        }

        $shopId = WaiMaiShopService::getThirdMappingCache($thirdShopId, $this->sourceType);

        if ($orderId && !$shopId) {
            $shopId = WaiMaiOrderIdShopIdMappingModel::getShopId($orderId);
        }

        BaseWaiMaiOrderService::$callBackShopId = $shopId;
        BaseWaiMaiOrderService::$callBackId     = WaiMaiCallbackLogModel::addData($this->sourceType, (int)$orderId,
            (string)$thirdShopId, json_encode($request->all(), JSON_UNESCAPED_UNICODE), $route, (int)$shopId);

        if (!$shopId) {
            Log::channel('waiMai')->info("DyWaiMaiCheckSign notFoundShopId:", [
                'request'     => $requestData,
                'orderId'     => $orderId,
                'thirdShopId' => $thirdShopId,
                'shopId'      => $shopId,
                'logId'       => $logId,
                'msgId'       => $msgId,
            ]);

            BuildFs::newRobot(SendFs::ROBOT_BUSINESS_WARING)->setTitleContent('抖音外卖回调接口未找到店铺id', json_encode([
                'request'     => $requestData,
                'orderId'     => $orderId,
                'thirdShopId' => $thirdShopId,
                'shopId'      => $shopId,
                'logId'       => $logId,
                'msgId'       => $msgId,
            ]))->send();
            return response()->json(['data' => 'ok'])->header('Content-Type', 'application/json;charset=utf-8');
        }

        return $next($request);
    }

    /**
     * 签名校验
     * @param Request $request
     * @param string $route
     * @param string $sign
     * @param string $logId
     * @param string $timestamp
     * @param string $msgId
     * @return bool
     * @throws \Exception
     */
    public function checkSign(
        Request $request,
        string $route,
        string $sign,
        string $logId = '',
        string $timestamp = '',
        string $msgId = ''
    ): bool {
        $resultJson   = $request->getContent();
        $query        = $request->query();
        $clientSecret = DyWaiMai::CLIENT_SECRET;
        if (strpos($route, '/api/pay/callback/dyWaiMai/takeoutRefundNotice') !== false) {
            $parArr = $query;
            if (isset($parArr['sign'])) {
                unset($parArr['sign']);
            }
            if ($resultJson) {
                $parArr['http_body'] = $resultJson;
            }
            $str     = $clientSecret . '&' . urldecode(http_build_query($parArr));
            $newSign = hash('sha256', $str);
        } else {
            $newSign = sha1($clientSecret . $resultJson);
        }


        if ($newSign != $sign) {
            Log::channel('waiMai')->info("DyWaiMaiCheckSign Err:", [
                'request'         => $request->all(),
                'resultJson'      => $resultJson,
                'query'           => $query,
                'douyinSignature' => $sign,
                'newSign'         => $newSign,
                'logId'           => $logId,
                'msgId'           => $msgId,
                'route'           => $route,
            ]);
            BuildFs::newRobot(SendFs::ROBOT_BUSINESS_WARING)->setTitleContent('抖音外卖回调接口验签失败', json_encode([
                'request'         => $request->all(),
                'route'           => $route,
                'douyinSignature' => $sign,
                'newSign'         => $newSign,
                'logId'           => $logId,
                'msgId'           => $msgId,
            ]))->send();
            return false;
        }
        return true;
    }

    /**
     * 判断 msgId 是否重复
     * @param Request $request
     * @param string $msgId
     * @return bool
     */
    public function checkMsgId(Request $request, string $msgId): bool
    {
        // msgId 去重
        $msgIdKey = "DyWaiMaiCheckSign:{$msgId}";
        if (!Cache::has($msgIdKey)) {
            Cache::remember($msgIdKey, 30 * 86400,
                function () use ($request) {
                    return ['request' => $request->all()];
                });
            return false;
        } else {
            Log::channel('waiMai')->info("DyWaiMaiCheckSign duplicateMsg:", [
                'request' => $request->all(),
                'msgId'   => $msgId,
            ]);
            return true;
        }
    }
}
